﻿/*
   Copyright 2012 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Flower.Services.Data;
using Jurassic.Library;
using NUnit.Framework;
using Flower.Directory.Scripting;
using Jurassic;
using System.Collections.ObjectModel;
using Flower.Directory.Scripting.DirectoryEntries;

namespace Flower.Directory.Scripting.Tests
{
    [TestFixture]
    public class MappingTests
    {
        private ScriptEngine _engine;

        [SetUp]
        public void Setup()
        {
            _engine = Scripting.Setup.CreateEngine();
        }

        #region Setters and getters

        [Test]
        public void SetString()
        {
            ObjectInstance obj = _engine.Object.Construct();
            obj.Set("prop", "abc");
            Assert.IsTrue(obj.GetString("prop") == "abc");

            obj = _engine.Object.Construct();
            obj.Set("prop", (string)null);
            Assert.IsTrue(obj.GetString("prop") == null);

            obj = _engine.Object.Construct();
            obj.Set("prop", 1);
            Assert.Throws<PropertyMappingException>(() => { var str = obj.GetString("prop"); });
        }

        [Test]
        public void GetString()
        {
            var obj = _engine.Evaluate<ObjectInstance>("obj = { prop: 'abc' }");
            Assert.IsTrue(obj.GetString("prop") == "abc");

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: new String('abc') }");
            Assert.IsTrue(obj.GetString("prop") == "abc");

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop1: new String('abc') }");
            Assert.IsTrue(obj.GetString("prop") == null);

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: null }");
            Assert.IsTrue(obj.GetString("prop") == null);

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: 1 }");
            Assert.Throws<PropertyMappingException>(() => { var str = obj.GetString("prop"); });
        }

        [Test]
        public void SetInt()
        {
            ObjectInstance obj = _engine.Object.Construct();
            obj.Set("prop", 1);
            Assert.IsTrue(obj.GetInt("prop") == 1);

            obj = _engine.Object.Construct();
            obj.Set("prop", (string)null);
            Assert.Throws<PropertyMappingException>(() => { var num = obj.GetInt("prop"); });
        }

        [Test]
        public void GetInt()
        {
            var obj = _engine.Evaluate<ObjectInstance>("obj = { prop: 1 }");
            Assert.IsTrue(obj.GetInt("prop") == 1);

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: new Number(1) }");
            Assert.IsTrue(obj.GetInt("prop") == 1);

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: null }");
            Assert.Throws<PropertyMappingException>(() => { var num = obj.GetInt("prop"); });

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: 'abc' }");
            Assert.Throws<PropertyMappingException>(() => { var num = obj.GetInt("prop"); });
        }

        [Test]
        public void SetDateTime()
        {
            ObjectInstance obj = _engine.Object.Construct();
            var date = new DateTime(2012, 1, 1, 12, 30, 15, 1);
            obj.Set("prop", date);
            Assert.IsTrue(obj.GetDateTime("prop") == date);

            obj = _engine.Object.Construct();
            obj.Set("prop", 1);
            Assert.Throws<PropertyMappingException>(() => { var d = obj.GetDateTime("prop"); });
        }

        [Test]
        public void GetDateTime()
        {
            var obj = _engine.Evaluate<ObjectInstance>("obj = { prop: new Date(2012, 0, 1) }");
            var date = new DateTime(2012, 1, 1);
            Assert.IsTrue(obj.GetDateTime("prop") == date);

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: null }");
            Assert.Throws<PropertyMappingException>(() => { var num = obj.GetDateTime("prop"); });

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: 'abc' }");
            Assert.Throws<PropertyMappingException>(() => { var num = obj.GetDateTime("prop"); });
        }

        [Test]
        public void SetStrings()
        {
            ObjectInstance obj = _engine.Object.Construct();
            obj.Set("prop", new[] { "a", "b", "c" });
            var coll = obj.GetStrings("prop");
            Assert.IsTrue(coll.Count == 3);
            Assert.IsTrue(coll.Any(el => el == "a"));
            Assert.IsTrue(coll.Any(el => el == "b"));
            Assert.IsTrue(coll.Any(el => el == "c"));
        }

        [Test]
        public void GetStrings()
        {
            var obj = _engine.Evaluate<ObjectInstance>("obj = { prop: ['a', 'b', 'c'] }");
            var coll = obj.GetStrings("prop");
            Assert.IsTrue(coll.Count == 3);
            Assert.IsTrue(coll.Any(el => el == "a"));
            Assert.IsTrue(coll.Any(el => el == "b"));
            Assert.IsTrue(coll.Any(el => el == "c"));

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: ['a', null, 'c'] }");
            coll = obj.GetStrings("prop");
            Assert.IsTrue(coll.Count == 3);
            Assert.IsTrue(coll.Any(el => el == "a"));
            Assert.IsTrue(coll.Any(el => el == null));
            Assert.IsTrue(coll.Any(el => el == "c"));

            obj = _engine.Evaluate<ObjectInstance>("obj = { prop: ['a', 1, 'c'] }");
            Assert.Throws<PropertyMappingException>(() => { coll = obj.GetStrings("prop"); });
        }

        [Test]
        public void SetAcl()
        {
            ObjectInstance obj = _engine.Object.Construct();
            obj.Set
            (
                "acl", 
                new Collection<AccessControlEntry>
                {
                    new AccessControlEntry
                    {
                        Role = "Role1",
                        Permission = Permission.Read
                    },
                    new AccessControlEntry
                    {
                        Role = "Role2",
                        Permission = Permission.Write
                    },
                    new AccessControlEntry
                    {
                        Role = "Role3",
                        Permission = Permission.Read | Permission.Write
                    },
                }
            );
            
            var coll = obj.GetAccessControlList("acl");

            Assert.IsTrue(coll.Any(perm => perm.Role == "Role1" && perm.Permission == Permission.Read));
            Assert.IsTrue(coll.Any(perm => perm.Role == "Role2" && perm.Permission == Permission.Write));
            Assert.IsTrue(coll.Any(perm => perm.Role == "Role3" && perm.Permission == (Permission.Read | Permission.Write)));
        }

        [Test]
        public void GetAcl()
        {
            var obj = _engine.Evaluate<ObjectInstance>
            (
                "a = { acl: [ " +
                "  { role: 'Role1', permission: 1 }, " +
                "  { role: 'Role2', permission: 2 }, " +
                "  { role: 'Role3', permission: 3 } " + 
                "] }"
            );

            var coll = obj.GetAccessControlList("acl");

            Assert.IsTrue(coll.Any(perm => perm.Role == "Role1" && perm.Permission == Permission.Read));
            Assert.IsTrue(coll.Any(perm => perm.Role == "Role2" && perm.Permission == Permission.Write));
            Assert.IsTrue(coll.Any(perm => perm.Role == "Role3" && perm.Permission == (Permission.Read | Permission.Write)));
        }

        [Test]
        public void SetXmlObject()
        {
            ObjectInstance obj = _engine.Object.Construct();

            var xml = new XmlObject(_engine.Object.InstancePrototype, "<root>text</root>", BlobFormat.TextXml);
            Blob blob = xml.ToBlob();

            obj.Set("xml", blob);

            Blob outBlob = obj.GetBlob("xml");

            Assert.IsTrue(blob.Format == outBlob.Format);
            Assert.IsTrue(blob.Data.Length == outBlob.Data.Length);
            Assert.IsTrue(outBlob.Data.Length > 0);

            for (int i = 0; i < blob.Data.Length; i++)
            {
                Assert.IsTrue(blob.Data[i] == outBlob.Data[i]);
            }
        }

        [Test]
        public void GetXmlObject()
        {
            var obj = _engine.Evaluate<ObjectInstance>("a = { xml: xml('<root>text</root>') }");
            Blob blob = obj.GetBlob("xml");

            Assert.IsTrue(blob.Data != null);
            Assert.IsTrue(blob.Data.Length > 0);
            Assert.IsTrue(blob.Format == BlobFormat.TextXml);

            obj = _engine.Evaluate<ObjectInstance>("a = { xml: xml('<root>text</root>', 2) }");
            blob = obj.GetBlob("xml");

            Assert.IsTrue(blob.Data != null);
            Assert.IsTrue(blob.Data.Length > 0);
            Assert.IsTrue(blob.Format == BlobFormat.GZipXml);
        }

        #endregion

        #region Entries

        [Test]
        public void Assembly()
        {
            const string script = @"
                new Assembly({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ]
                })
            ";

            var entryObject = (AssemblyObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Assembly)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(_engine.Evaluate<bool>("(new Assembly({})).getType() == DET_ASSEMBLY"));
        }

        [Test]
        public void Folder()
        {
            const string script = @"
                new Folder({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ]
                })
            ";

            var entryObject = (FolderObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Folder)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(_engine.Evaluate<bool>("(new Folder({})).getType() == DET_FOLDER"));
        }

        [Test]
        public void Link()
        {
            const string script = @"
                new Link({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    target: '#target'
                })
            ";

            var entryObject = (LinkObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Link)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.Target == "#target");
            Assert.IsTrue(_engine.Evaluate<bool>("(new Link({})).getType() == DET_LINK"));
        }

        [Test]
        public void Message()
        {
            const string script = @"
                new Message({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    data: xml('<root></root>')
                })
            ";

            var entryObject = (MessageObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Message)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.Data != null);
            Assert.IsTrue(entry.Data.Data != null);
            Assert.IsTrue(entry.Data.Data.Length > 0);
            Assert.IsTrue(_engine.Evaluate<bool>("(new Message({})).getType() == DET_MESSAGE"));
        }

        [Test]
        public void Process()
        {
            const string script = @"
                new Process({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    inputMessage: xml('<in></in>'),
                    outputMessage: xml('<out></out>'),
                    workflow: '#workflow'
                })
            ";

            var entryObject = (ProcessObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Process)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.Workflow == "#workflow");
            Assert.IsTrue(_engine.Evaluate<bool>("(new Process({})).getType() == DET_PROCESS"));
        }

        [Test]
        public void Processor()
        {
            const string script = @"
                new Processor({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    configuration: xml('<root></root>')
                })
            ";

            var entryObject = (ProcessorObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Processor)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.Configuration != null);
            Assert.IsTrue(entry.Configuration.Data != null);
            Assert.IsTrue(entry.Configuration.Data.Length > 0);
            Assert.IsTrue(_engine.Evaluate<bool>("(new Processor({})).getType() == DET_PROCESSOR"));
        }

        [Test]
        public void ServiceContainer()
        {
            const string script = @"
                new ServiceContainer({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    configuration: xml('<root></root>')
                })
            ";

            var entryObject = (ServiceContainerObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (ServiceContainer)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.Configuration != null);
            Assert.IsTrue(entry.Configuration.Data != null);
            Assert.IsTrue(entry.Configuration.Data.Length > 0);
            Assert.IsTrue(_engine.Evaluate<bool>("(new ServiceContainer({})).getType() == DET_SERVICE_CONTAINER"));
        }

        [Test]
        public void Set()
        {
            const string script = @"
                new Set({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    messageType: 'MessageType',
                    capacity: 1
                })
            ";

            var entryObject = (SetObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Set)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.MessageType == "MessageType");
            Assert.IsTrue(entry.Capacity == 1);
            Assert.IsTrue(_engine.Evaluate<bool>("(new Set({})).getType() == DET_SET"));
        }

        [Test]
        public void State()
        {
            const string script = @"
                new State({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    data: xml('<root></root>')
                })
            ";

            var entryObject = (StateObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (State)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.Data != null);
            Assert.IsTrue(entry.Data.Data != null);
            Assert.IsTrue(entry.Data.Data.Length > 0);
            Assert.IsTrue(_engine.Evaluate<bool>("(new State({})).getType() == DET_STATE"));
        }

        [Test]
        public void Workflow()
        {
            const string script = @"
                new Workflow({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    definitionType: 'DefinitionType',
                    preferredProcessors: ['Processor1', 'Processor2']
                })
            ";

            var entryObject = (WorkflowObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Services.Data.Workflow)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.DefinitionType == "DefinitionType");
            Assert.IsTrue(entry.PreferredProcessors != null);
            Assert.IsTrue(entry.PreferredProcessors.Any(p => p == "Processor1"));
            Assert.IsTrue(entry.PreferredProcessors.Any(p => p == "Processor2"));
            Assert.IsTrue(_engine.Evaluate<bool>("(new Workflow({})).getType() == DET_WORKFLOW"));
        }

        [Test]
        public void Role()
        {
            const string script = @"
                new Role({
                    id: '#id',
                    name: 'Name',
                    createdOn: new Date(2012, 0, 1),
                    updatedOn: new Date(2012, 0, 2),
                    acl: [ { role: 'role1', permission: 1 }, { role: 'role2', permission: 2 } ],
                    users: ['User1', 'User2']
                })
            ";

            var entryObject = (RoleObject)_engine.Evaluate<ObjectInstance>(script);
            var entry = (Role)entryObject.Entry;

            Assert.IsTrue(entry.Id == "#id");
            Assert.IsTrue(entry.Name == "Name");
            Assert.IsTrue(entry.CreatedOn == new DateTime(2012, 1, 1));
            Assert.IsTrue(entry.UpdatedOn == new DateTime(2012, 1, 2));
            Assert.IsTrue(entry.AccessControlList != null);
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role1" && r.Permission == Permission.Read));
            Assert.IsTrue(entry.AccessControlList.Any(r => r.Role == "role2" && r.Permission == Permission.Write));
            Assert.IsTrue(entry.Users != null);
            Assert.IsTrue(entry.Users.Any(p => p == "User1"));
            Assert.IsTrue(entry.Users.Any(p => p == "User2"));
            Assert.IsTrue(_engine.Evaluate<bool>("(new Role({})).getType() == DET_ROLE"));
        }
        #endregion
    }
}
